using System;
using System.Data;
using System.Data.Common;
using OracleInternal.Common;
using OracleInternal.ServiceObjects;

namespace Oracle.ManagedDataAccess.Client
{
	public sealed class OracleTransaction : DbTransaction
	{
		internal OracleTransactionImpl m_oracleTransactionImpl;

		internal OracleConnection m_connection;

		private OracleCommand m_command;

		private bool m_completed;

		private bool m_bClosed;

		private IsolationLevel m_isolationLevel;

		public new OracleConnection Connection => m_connection;

		public override IsolationLevel IsolationLevel
		{
			get
			{
				if (m_completed || m_bClosed)
				{
					throw new InvalidOperationException(OracleStringResourceManager.GetErrorMesg(ResourceStringConstants.OBJECT_CLOSED));
				}
				return m_isolationLevel;
			}
		}

		internal bool Completed => m_completed;

		protected override DbConnection DbConnection => m_connection;

		internal OracleTransaction(OracleConnection con, IsolationLevel isolationLevel)
		{
			if (ProviderConfig.m_bTraceLevelPrivate)
			{
				Trace.Write(OracleTraceLevel.Private, OracleTraceTag.Entry, OracleTraceClassName.OracleTransaction, OracleTraceFuncName.ctor);
			}
			try
			{
				m_connection = con;
				m_isolationLevel = isolationLevel;
				m_connection.m_oracleConnectionImpl.SetAutoCommit(bAutoCommitOn: false);
				m_oracleTransactionImpl = new OracleTransactionImpl(con.m_oracleConnectionImpl, isolationLevel);
			}
			catch (Exception ex)
			{
				OracleException.HandleError(OracleTraceLevel.Private, OracleTraceTag.Error, OracleTraceClassName.OracleTransaction, OracleTraceFuncName.ctor, ex);
				throw;
			}
			finally
			{
				if (ProviderConfig.m_bTraceLevelPrivate)
				{
					Trace.Write(OracleTraceLevel.Private, OracleTraceTag.Exit, OracleTraceClassName.OracleTransaction, OracleTraceFuncName.ctor);
				}
			}
		}

		public override void Commit()
		{
			if (ProviderConfig.m_bTraceLevelPublic)
			{
				Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Entry, OracleTraceClassName.OracleTransaction, OracleTraceFuncName.Commit);
			}
			OracleLogicalTransaction oracleLogicalTransaction = null;
			try
			{
				if (m_completed || m_bClosed)
				{
					throw new InvalidOperationException(OracleStringResourceManager.GetErrorMesg(ResourceStringConstants.OBJECT_CLOSED));
				}
				try
				{
					m_oracleTransactionImpl.Commit(m_connection, ref oracleLogicalTransaction);
					m_connection.CheckForWarnings(this);
					m_completed = true;
				}
				finally
				{
					m_connection.m_oracleConnectionImpl.SetAutoCommit(bAutoCommitOn: true);
					if (ConfigBaseClass.m_bLegacyIsolationLevelBehavior && IsolationLevel.Serializable == m_isolationLevel)
					{
						m_connection.m_oracleConnectionImpl.SwitchIsolationLevel(IsolationLevel.ReadCommitted);
					}
				}
				Dispose();
			}
			catch (Exception ex)
			{
				OracleException.HandleError(OracleTraceLevel.Public, OracleTraceTag.Error, OracleTraceClassName.OracleTransaction, OracleTraceFuncName.Commit, ex, oracleLogicalTransaction);
				if (ex is OracleException)
				{
					if (((OracleException)ex).OracleLogicalTransaction != null && ((OracleException)ex).OracleLogicalTransaction.UserCallCompleted == true && ((OracleException)ex).OracleLogicalTransaction.Committed == true)
					{
						return;
					}
					throw;
				}
				throw;
			}
			finally
			{
				if (ProviderConfig.m_bTraceLevelPublic)
				{
					Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Exit, OracleTraceClassName.OracleTransaction, OracleTraceFuncName.Commit);
				}
			}
		}

		public override void Rollback()
		{
			if (ProviderConfig.m_bTraceLevelPublic)
			{
				Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Entry, OracleTraceClassName.OracleTransaction, OracleTraceFuncName.Rollback);
			}
			OracleLogicalTransaction oracleLogicalTransaction = null;
			try
			{
				if (m_completed || m_bClosed)
				{
					throw new InvalidOperationException(OracleStringResourceManager.GetErrorMesg(ResourceStringConstants.OBJECT_CLOSED));
				}
				try
				{
					m_oracleTransactionImpl.Rollback(m_connection, ref oracleLogicalTransaction);
					m_connection.CheckForWarnings(this);
					m_completed = true;
				}
				finally
				{
					m_connection.m_oracleConnectionImpl.SetAutoCommit(bAutoCommitOn: true);
					if (ConfigBaseClass.m_bLegacyIsolationLevelBehavior && IsolationLevel.Serializable == m_isolationLevel)
					{
						m_connection.m_oracleConnectionImpl.SwitchIsolationLevel(IsolationLevel.ReadCommitted);
					}
				}
				Dispose();
			}
			catch (Exception ex)
			{
				OracleException.HandleError(OracleTraceLevel.Public, OracleTraceTag.Error, OracleTraceClassName.OracleTransaction, OracleTraceFuncName.Rollback, ex, oracleLogicalTransaction);
				if (ex is OracleException)
				{
					if (((OracleException)ex).OracleLogicalTransaction != null && ((OracleException)ex).OracleLogicalTransaction.UserCallCompleted == true && ((OracleException)ex).OracleLogicalTransaction.Committed == true)
					{
						return;
					}
					throw;
				}
				throw;
			}
			finally
			{
				if (ProviderConfig.m_bTraceLevelPublic)
				{
					Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Exit, OracleTraceClassName.OracleTransaction, OracleTraceFuncName.Rollback);
				}
			}
		}

		public void Rollback(string savepointName)
		{
			if (ProviderConfig.m_bTraceLevelPublic)
			{
				Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Entry, OracleTraceClassName.OracleTransaction, OracleTraceFuncName.Rollback);
			}
			try
			{
				if (m_completed || m_bClosed)
				{
					throw new InvalidOperationException(OracleStringResourceManager.GetErrorMesg(ResourceStringConstants.OBJECT_CLOSED));
				}
				if (m_command == null)
				{
					m_command = new OracleCommand("", m_connection);
				}
				m_command.Parameters.Add("sp", savepointName);
				m_command.CommandText = "DECLARE  sp_name VARCHAR2(128 char); query VARCHAR2(156 char);  BEGIN  sp_name:=SYS.DBMS_ASSERT.SIMPLE_SQL_NAME(:sp);  query := 'ROLLBACK TO SAVEPOINT ' || sp_name; EXECUTE IMMEDIATE query;  END;";
				m_command.CommandTimeout = 0;
				m_command.ExecuteNonQuery();
			}
			catch (Exception ex)
			{
				OracleException.HandleError(OracleTraceLevel.Public, OracleTraceTag.Error, OracleTraceClassName.OracleTransaction, OracleTraceFuncName.Rollback, ex);
				throw;
			}
			finally
			{
				if (m_command != null)
				{
					m_command.Parameters.Clear();
				}
				if (ProviderConfig.m_bTraceLevelPublic)
				{
					Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Exit, OracleTraceClassName.OracleTransaction, OracleTraceFuncName.Rollback);
				}
			}
		}

		public void Save(string savepointName)
		{
			if (ProviderConfig.m_bTraceLevelPublic)
			{
				Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Entry, OracleTraceClassName.OracleTransaction, OracleTraceFuncName.Save);
			}
			try
			{
				if (m_completed || m_bClosed)
				{
					throw new InvalidOperationException(OracleStringResourceManager.GetErrorMesg(ResourceStringConstants.OBJECT_CLOSED));
				}
				if (m_command == null)
				{
					m_command = new OracleCommand("", m_connection);
				}
				m_command.Parameters.Add("sp", savepointName);
				m_command.CommandText = "DECLARE  sp_name VARCHAR2(128 char); query VARCHAR2(140 char) ;  BEGIN  sp_name:=SYS.DBMS_ASSERT.SIMPLE_SQL_NAME(:sp);  query := 'SAVEPOINT ' || sp_name; EXECUTE IMMEDIATE query; END;";
				m_command.CommandTimeout = 0;
				m_command.ExecuteNonQuery();
			}
			catch (Exception ex)
			{
				OracleException.HandleError(OracleTraceLevel.Public, OracleTraceTag.Error, OracleTraceClassName.OracleTransaction, OracleTraceFuncName.Save, ex);
				throw;
			}
			finally
			{
				if (m_command != null)
				{
					m_command.Parameters.Clear();
				}
				if (ProviderConfig.m_bTraceLevelPublic)
				{
					Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Exit, OracleTraceClassName.OracleTransaction, OracleTraceFuncName.Save);
				}
			}
		}

		internal void ConnectionClose()
		{
			if (ProviderConfig.m_bTraceLevelPrivate)
			{
				Trace.Write(OracleTraceLevel.Private, OracleTraceTag.Entry, OracleTraceClassName.OracleTransaction, OracleTraceFuncName.ConnectionClose);
			}
			try
			{
				Dispose(disposing: true);
			}
			catch (Exception ex)
			{
				OracleException.HandleError(OracleTraceLevel.Private, OracleTraceTag.Error, OracleTraceClassName.OracleTransaction, OracleTraceFuncName.ConnectionClose, ex);
				throw;
			}
			finally
			{
				if (ProviderConfig.m_bTraceLevelPrivate)
				{
					Trace.Write(OracleTraceLevel.Private, OracleTraceTag.Exit, OracleTraceClassName.OracleTransaction, OracleTraceFuncName.ConnectionClose);
				}
			}
		}

		public new void Dispose()
		{
			Dispose(disposing: true);
		}

		protected override void Dispose(bool disposing)
		{
			if (ProviderConfig.m_bTraceLevelPublic)
			{
				Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Entry, OracleTraceClassName.OracleTransaction, OracleTraceFuncName.Dispose);
			}
			try
			{
				if (m_bClosed)
				{
					return;
				}
				try
				{
					if (!m_completed)
					{
						Rollback();
						return;
					}
				}
				catch (Exception ex)
				{
					OracleException.HandleError(OracleTraceLevel.Public, OracleTraceTag.Error, OracleTraceClassName.OracleTransaction, OracleTraceFuncName.Dispose, ex);
				}
				if (disposing)
				{
					m_connection.m_oraTransaction = null;
					m_connection = null;
					if (m_command != null)
					{
						try
						{
							m_command.Dispose();
						}
						catch
						{
						}
						m_command = null;
					}
				}
				m_bClosed = true;
			}
			finally
			{
				GC.SuppressFinalize(this);
				if (ProviderConfig.m_bTraceLevelPublic)
				{
					Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Exit, OracleTraceClassName.OracleTransaction, OracleTraceFuncName.Dispose);
				}
			}
		}

		~OracleTransaction()
		{
			try
			{
				Dispose(disposing: false);
			}
			catch (Exception ex)
			{
				OracleException.HandleError(OracleTraceLevel.Public, OracleTraceTag.Error, OracleTraceClassName.OracleTransaction, OracleTraceFuncName.dtor, ex);
			}
		}
	}
}
